package com.crop.back.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.crop.back.common.enums.Deleted;
import com.crop.back.common.enums.RoleEnum;
import com.crop.back.common.utils.MD5Util;
import com.crop.back.common.utils.MailUtil;
import static com.crop.back.common.utils.RedisConstants.LOGIN_CODE_KEY;
import static com.crop.back.common.utils.RedisConstants.LOGIN_CODE_TTL;
import static com.crop.back.common.utils.RedisConstants.LOGIN_TOKEN_KEY;
import static com.crop.back.common.utils.RedisConstants.LOGIN_TOKEN_TTL;
import com.crop.back.common.utils.RedisUtil;
import com.crop.back.common.utils.ResultBean;
import com.crop.back.common.utils.UserHolder;
import com.crop.back.common.validator.group.sys.RegisterGroup;
import com.crop.back.dto.UserDTO;
import com.crop.back.dto.UserDTO2;
import com.crop.back.entity.Processor;
import com.crop.back.entity.Product;
import com.crop.back.entity.Role;
import com.crop.back.entity.Supplier;
import com.crop.back.entity.User;
import com.crop.back.service.ProcessorService;
import com.crop.back.service.RoleService;
import com.crop.back.service.SupplierService;
import com.crop.back.service.UserService;
import com.crop.back.vo.EmailLoginVo;
import com.crop.back.vo.EmailRegisterVo;
import com.crop.back.vo.MailVo;
import com.crop.back.vo.NamePwdLoginVo;
import com.crop.back.vo.UpdatePasswordVo;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.Mapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

/**
 * <p>
 * 用户表 前端控制器
 * </p>
 *
 * @author 作者
 * @since 2025-01-20
 */
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {

    @Resource
    private UserService userService;
    @Resource
    private RoleService roleService;

    @Resource
    private SupplierService supplierService;
    @Resource
    private ProcessorService processorService;
    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private MailUtil mailUtil;

    private static final String FROM_EMAIL = "yeaonaiwohe1212@163.com";

    @PostMapping("/list")
    public ResultBean getAll(@RequestBody UserDTO2 userDTO) {
        Map<Long, String> map = roleService.list(new LambdaQueryWrapper<Role>()
                        .eq(Role::getDeleted, Deleted.NOT_DELETED.getCode()))
                .stream()
                .collect(Collectors.toMap(
                        Role::getId,
                        Role::getRoleName
                ));
        Long userId = UserHolder.getUser().getId();
        Page<User> pageInfo = new Page<>(userDTO.getPage(), userDTO.getPageSize());
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getDeleted, Deleted.NOT_DELETED.getCode());
        queryWrapper.ne(User::getId, userId);
        Page<User> page = userService.page(pageInfo, queryWrapper);
        List<UserDTO2> collect = page.getRecords().stream()
                .map(item -> {
                    UserDTO2 dto = BeanUtil.copyProperties(item, UserDTO2.class);
                    dto.setRoleName(map.get(dto.getRoleId()));
                    return dto;
                }).collect(Collectors.toList());
        return ResultBean.ok(collect, page.getTotal());
    }

    @PostMapping("/get")
    public ResultBean getByCondition(@RequestBody UserDTO2 userDTO) {
        Map<Long, String> map = roleService.list(new LambdaQueryWrapper<Role>()
                        .eq(Role::getDeleted, Deleted.NOT_DELETED.getCode()))
                .stream()
                .collect(Collectors.toMap(
                        Role::getId,
                        Role::getRoleName
                ));
        Long userId = UserHolder.getUser().getId();
        Page<User> pageInfo = new Page<>(userDTO.getPage(), userDTO.getPageSize());
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getDeleted, Deleted.NOT_DELETED.getCode());
        queryWrapper.ne(User::getId, userId);
        queryWrapper.like(userDTO.getUserName() != null, User::getUserName, userDTO.getUserName());
        queryWrapper.like(userDTO.getEmail() != null, User::getEmail, userDTO.getEmail());
        queryWrapper.like(userDTO.getPhone() != null, User::getPhone, userDTO.getPhone());
        Page<User> page = userService.page(pageInfo, queryWrapper);
        List<UserDTO2> collect = page.getRecords().stream()
                .map(item -> {
                    UserDTO2 dto = BeanUtil.copyProperties(item, UserDTO2.class);
                    dto.setRoleName(map.get(dto.getRoleId()));
                    return dto;
                }).collect(Collectors.toList());
        return ResultBean.ok(collect, page.getTotal());
    }

    @PostMapping("/save")
    public ResultBean save(@RequestBody UserDTO2 userDTO) {
        try {
            User user = BeanUtil.copyProperties(userDTO, User.class);
            String password = userDTO.getPassword();
            if (password.length() <= 20) {
                user.setPassword(MD5Util.encryptMD5(password));
            }
            if (user.getId() == null) {
                return ResultBean.ok(userService.save(user));
            } else {
                return ResultBean.ok(userService.updateById(user));
            }
        } catch (Exception e) {
            if (e.getMessage().contains("Duplicate entry") || e.getCause().getMessage().contains("Duplicate entry")) {
                String errorMessage = e.getMessage();
                if (errorMessage.contains("phone")) {
                    return ResultBean.fail("手机号已存在，请检查重复项");
                } else if (errorMessage.contains("email")) {
                    return ResultBean.fail("邮箱已存在，，请检查重复项");
                } else if (errorMessage.contains("name")) {
                    return ResultBean.fail("用户名称已存在，，请检查重复项");
                } else {
                    return ResultBean.fail("数据已存在，请检查重复项");
                }
            }
        }
        return ResultBean.fail("保存失败");
    }

    @DeleteMapping("/delete/{id}")
    public ResultBean delete(@PathVariable("id") Long id) {
        try {

            userService.removeById(id);
            return ResultBean.ok();
        } catch (Exception e) {
            return ResultBean.fail("删除失败");
        }
    }

    @GetMapping("/role")
    public ResultBean getRole() {
        Map<Long, String> map = roleService.list(new LambdaQueryWrapper<Role>()
                        .eq(Role::getDeleted, Deleted.NOT_DELETED.getCode()))
                .stream()
                .collect(Collectors.toMap(
                        Role::getId,
                        Role::getRoleName
                ));
        String name = UserHolder.getUser().getUserName();
        User user = userService.getOne(new LambdaQueryWrapper<User>()
                .eq(User::getDeleted, Deleted.NOT_DELETED.getCode())
                .eq(name != null, User::getUserName, name));
        return ResultBean.ok(map.get(user.getRoleId()));
    }

    @ApiOperation(value = "使用用户名、密码登录")
    @PostMapping("/namePwdLogin")
    public ResultBean namePwdLogin(@RequestBody NamePwdLoginVo namePwdLoginVo) {
        try {
            String name = namePwdLoginVo.getName();
            String password = namePwdLoginVo.getPassword();
            return pwdLogin(name, password);
        } catch (Exception e) {
            return ResultBean.fail("登录失败");
        }
    }

    private ResultBean pwdLogin(String name, String pwd) {
        // 新增查询条件
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();

        queryWrapper.eq(User::getUserName, name);
        queryWrapper.eq(User::getDeleted, Deleted.NOT_DELETED.getCode());
        // 添加密码条件，密码进行 MD5 加密后再与数据库数据比较
        queryWrapper.eq(User::getPassword, MD5Util.encryptMD5(pwd));
        // 获取用户数据
        User user = userService.getOne(queryWrapper);
        // 如果存在用户数据
        if (user == null) {
            // 不存在，登陆失败
            return ResultBean.fail("用户不存在或密码错误登录失败");
        }
        // 保存用户信息到 redis中
        // 随机生成token，作为登录令牌
        String token = UUID.randomUUID().toString(true);
        // 将AdminDTO对象转为HashMap存储
        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
        setReferenceId(userDTO);
        Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
                CopyOptions.create()
                        .setIgnoreNullValue(true)
                        .setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));
        // 存储 设置token有效期
        String tokenKey = LOGIN_TOKEN_KEY + token;
        redisUtil.hmsetWithTimeUnit(tokenKey, userMap, LOGIN_TOKEN_TTL, TimeUnit.SECONDS);
        // 返回token
        return ResultBean.ok(token);
    }

    @ApiOperation(value = "邮箱验证码登录")
    @PostMapping("/emailLogin")
    public ResultBean emailLogin(@RequestBody EmailLoginVo emailLoginVo) {
        String email = emailLoginVo.getEmail();
        String code = emailLoginVo.getCode();
        return codeLogin(email, code);
    }

    private ResultBean codeLogin(String email, String code) {
        // 获取 redis 中存放的验证码
        String redisCode = redisUtil.get(LOGIN_CODE_KEY + email);
        // 存在验证码，且输入的验证码与 redis 存放的验证码相同,则根据手机号去数据库查询数据
        if (StringUtils.isNotEmpty(redisCode) && code.equals(redisCode)) {
            // 新增查询条件
            LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
            // 根据邮箱去查询数据
            queryWrapper.eq(User::getEmail, email);
            queryWrapper.eq(User::getDeleted, Deleted.NOT_DELETED.getCode());
            User user = userService.getOne(queryWrapper);

            if (user == null) {
                return ResultBean.fail("用户不存在登录失败");
            }

            UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
            Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
                    CopyOptions.create()
                            .setIgnoreNullValue(true)
                            .setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));
            String token = UUID.randomUUID().toString(true);
            String tokenKey = LOGIN_TOKEN_KEY + token;
            redisUtil.hmsetWithTimeUnit(tokenKey, userMap, LOGIN_TOKEN_TTL, TimeUnit.SECONDS);
            // 返回token
            return ResultBean.ok(token);
        }
        return ResultBean.fail("验证码有误登录失败");
    }

    @ApiOperation(value = "发送邮箱验证码")
    @GetMapping("/sendEmailCode")
    public ResultBean sendEmailCode(@RequestParam String email) {
        try {
            // 生成6位随机验证码
            String code = RandomUtil.randomNumbers(6);

            // 构建邮件内容
            MailVo mailVo = new MailVo();
            mailVo.setFrom(FROM_EMAIL);
            mailVo.setTo(new String[] {email});
            mailVo.setCc(new String[] {});
            mailVo.setBcc(new String[] {});
            mailVo.setSubject("农作物溯源系统注册验证码");
            mailVo.setText("您的注册验证码是：" + code + "，有效期5分钟，请勿泄露给他人。");

            // 发送邮件
            mailUtil.sendMail(mailVo);

            // 将验证码存入Redis，设置5分钟过期
            redisUtil.setWithTimeUnit(LOGIN_CODE_KEY + email, code, LOGIN_CODE_TTL, TimeUnit.SECONDS);

            return ResultBean.ok("验证码发送成功");
        } catch (Exception e) {
            return ResultBean.fail("验证码发送失败");
        }
    }

    @ApiOperation(value = "邮箱验证码注册")
    @PostMapping("/emailRegister")
    public ResultBean emailRegister(@Validated({RegisterGroup.class}) @RequestBody EmailRegisterVo emailRegisterVo) {
        if (saveWithEmail(emailRegisterVo)) {
            return ResultBean.ok("用户注册成功");
        }
        return ResultBean.fail("用户注册失败");
    }

    @Transactional
    public boolean saveWithEmail(EmailRegisterVo emailRegisterVo) {
        // 判断 redis 中是否存在验证码
        String code = redisUtil.get(LOGIN_CODE_KEY + emailRegisterVo.getEmail());
        // redis 中存在验证码且与当前验证码相同
        if (StringUtils.isNotEmpty(code) && code.equals(emailRegisterVo.getCode())) {
            // 检查用户名是否已存在
            LambdaQueryWrapper<User> nameQuery = new LambdaQueryWrapper<>();
            nameQuery.eq(emailRegisterVo.getUserName() != null, User::getUserName, emailRegisterVo.getUserName());
            if (userService.count(nameQuery) > 0) {
                throw new RuntimeException("用户名已存在");
            }

            // 检查邮箱是否已存在
            LambdaQueryWrapper<User> emailQuery = new LambdaQueryWrapper<>();
            emailQuery.eq(User::getEmail, emailRegisterVo.getEmail());
            if (userService.count(emailQuery) > 0) {
                throw new RuntimeException("邮箱已被注册");
            }

            // 检查手机是否已存在
            LambdaQueryWrapper<User> phoneQuery = new LambdaQueryWrapper<>();
            phoneQuery.eq(emailRegisterVo.getPhone() != null, User::getEmail, emailRegisterVo.getEmail());
            if (userService.count(phoneQuery) > 0) {
                throw new RuntimeException("手机号已被注册");
            }
            User user = BeanUtil.copyProperties(emailRegisterVo, User.class);
            String password = user.getPassword();
            String encryt = MD5Util.encryptMD5(password);
            user.setPassword(encryt);
            user.setReferenceId(-1L);
            if (user.getRoleId() == RoleEnum.SUPPLIER.getCode()) {
                Supplier supplier = BeanUtil.copyProperties(user, Supplier.class);
                supplier.setName(user.getUserName());
                supplierService.save(supplier);
                user.setReferenceId(supplier.getId());
            } else if (user.getRoleId() == RoleEnum.PROCESSOR.getCode()) {
                Processor processor = BeanUtil.copyProperties(user, Processor.class);
                processor.setName(user.getUserName());
                processorService.save(processor);
                user.setReferenceId(processor.getId());
            }
            userService.save(user);
            return true;
        }
        return false;
    }

    @ApiOperation(value = "用户登出")
    @GetMapping("/logout")
    public ResultBean logout(HttpServletRequest request) {
        String token = request.getHeader("authorization");
        if (StrUtil.isBlank(token)) {
            return ResultBean.fail("登出失败");
        }
        redisUtil.del(LOGIN_TOKEN_KEY + token);
        return ResultBean.ok("登出成功");
    }

    @PostMapping("/updatePassword")
    public ResultBean updatePassword(@RequestBody UpdatePasswordVo updatePassword) {
        String name = updatePassword.getUserName();
        String oldPassword = updatePassword.getOldPassword();
        String newPassword = updatePassword.getNewPassword();

        // 查询用户信息
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUserName, name);
        User user = userService.getOne(queryWrapper);
        if (user == null) {
            return ResultBean.fail("修改失败");
        }
        // 验证原密码
        String encryptedPassword = MD5Util.encryptMD5(oldPassword);
        if (!Objects.equals(encryptedPassword, user.getPassword())) {
            return ResultBean.fail("原密码错误");
        }

        // 更新新密码
        LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(User::getUserName, name);
        updateWrapper.set(User::getPassword, MD5Util.encrypt(newPassword));
        userService.update(updateWrapper);
        return ResultBean.ok("密码修改成功");
    }

    private void setReferenceId(UserDTO userDTO) {
        Long roleId = userDTO.getRoleId();
        if (roleId == RoleEnum.SUPPLIER.getCode()) {
            LambdaQueryWrapper<Supplier> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Supplier::getName, userDTO.getUserName());
            Supplier one = supplierService.getOne(queryWrapper);
            if (one != null) {
                userDTO.setReferenceId(one.getId());
            } else {
                log.error("用户id为: " + userDTO.getId() + " 的供应商不存在");
            }
        } else if (roleId == RoleEnum.PROCESSOR.getCode()) {
            LambdaQueryWrapper<Processor> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Processor::getName, userDTO.getUserName());
            Processor one = processorService.getOne(queryWrapper);
            if (one != null) {
                userDTO.setReferenceId(one.getId());
            } else {
                log.error("用户id为：" + userDTO.getId() + " 的加工企业不存在");
            }
        }else {
            userDTO.setReferenceId(-1L);
        }
    }
}
